Blogs > OSGi Factory Configuration cardinality and limit

AEM Sites

OSGi Factory Configuration cardinality and limit

Infodales Tech Solutions | August 17, 2024

Happy to find you here. Welcome to another learning.

Use case for OSGi Factory Configuration

Lets say we have a configuration and we need it to have different values for its attributes defined for different scenarios. Speaking of a hypothetical scenario, say you are dealing with multiple api calls whose urls and timeout period are consumed via OSGi configurations. You need to create a separate configuration each having the same attributes and same attribute definations which are api url and timeout period. In this case aem provides a facility to group them all to be various instances of one OSGi configuration which is declared as an OSGi factory.

Creation of OSGi Configuration Factory

  • Lets begin by creating an interface called ApiHandlerService, where you will add the methods which you want to find in the configuration.

                                    
                                        package com.local.core.services;
    
                                        public interface ApiHandlerService {
                                            String getApiEndpont();
                                            int getTimeout();
                                            String callApi();
                                        }
    
                                    
                                


  • Now we need to create the respective object class defination for the same interface. We need to add the name to the same. This is the name from which we will be able to find configuration in the felix console.

    Then for each method, we need to add the attribute defination, consisting of two properties, name and description.

    We can also add the default value for the method, the way we have added timeout.

    Also we have added limit to the timeout by fixing its range.

                                    
                                        package com.local.core.services;
    
                                        import org.osgi.service.metatype.annotations.AttributeDefinition;
                                        import org.osgi.service.metatype.annotations.ObjectClassDefinition;
    
                                        @ObjectClassDefinition(
                                                name = "Api Handeler Configuration"
                                        )
                                        public @interface ApiHandlerConfig {
                                            @AttributeDefinition(
                                                    name = "Api Endpoint",
                                                    description = "This provides the enpoint url"
                                            )
                                            String apiEndpoint();
    
                                            @AttributeDefinition(
                                                    name = "Timeout in seconds",
                                                    description = "This provides the timeout in seconds",
                                                    min = "1",
                                                    max = "60"
                                            )
                                            int timeout() default 50;
                                        }
    
                                    
                                
  • After creating the object class defination, we need to create the implementation class. In Component annotation, provide the property configurationPolicy = ConfigurationPolicy.REQUIRE. This is added for factory configuration. Also in Designate annotation, provide a property factory = true. This ensures successful creation of the factory configuration.

    In callApi() method, we are actually consuming the values set in an instance of the configuration factory.

                                
                                    package com.local.core.services.impl;
                                    import com.local.core.services.ApiHandlerConfig;
                                    import com.local.core.services.ApiHandlerService;
                                    import org.apache.sling.caconfig.annotation.Configuration;
                                    import org.osgi.service.component.annotations.Activate;
                                    import org.osgi.service.component.annotations.Component;
                                    import org.osgi.service.component.annotations.ConfigurationPolicy;
                                    import org.osgi.service.component.annotations.Modified;
                                    import org.osgi.service.metatype.annotations.Designate;
    
                                    @Component(
                                        service = ApiHandlerService.class,
                                        configurationPolicy = ConfigurationPolicy.REQUIRE, // This is for factory configuration
                                        immediate = true
                                    )
                                    @Designate(ocd= ApiHandlerConfig.class, factory = true)
                                    public class ApiHandlerServiceImpl implements ApiHandlerService {
    
                                        private String apiEndpoint;
                                        private int timeout;
    
                                        @Activate
                                        @Modified
                                        protected void activate(ApiHandlerConfig config){
                                            this.apiEndpoint = config.apiEndpoint();
                                            this.timeout = config.timeout();
                                        }
    
    
                                        @Override
                                        public String getApiEndpont() {
                                            return "";
                                        }
    
                                        @Override
                                        public int getTimeout() {
                                            return 0;
                                        }
    
                                        @Override
                                        public String callApi() {
                                            return "Calling at API :: " + apiEndpoint + " with timeout :: " + timeout;
                                        }
                                    }
                                                                
                            
  • Now after creating the factory configuration, you will need to cisit the felix console to find the configuration factory in there with a + sign.

  • You need to add instances in the factory which will get stored with a unique Persistent Identity (PID). Here we have created two instances for the factory with fake api, one showing the todo and the other showing the recipies.

  • After creating these, we now will create a manager class which will iterate over the instances and create get the values entered for the endpoint and respective timeouts. Here in order to consume multiple values from the OSGi factory, we have created a list of type ApiHandlerService via Reference annotation with property cardinality = ReferenceCardinality.MULTIPLE. This tells the sling that with this reference, multiple services will be bound.

                                    
                                        package com.local.core.services.impl;
    
                                        import com.local.core.services.ApiHandlerService;
                                        import org.osgi.service.component.annotations.Component;
                                        import org.osgi.service.component.annotations.Reference;
                                        import org.osgi.service.component.annotations.ReferenceCardinality;
                                        import java.util.ArrayList;
                                        import java.util.List;
    
                                        @Component(
                                                service = ApiHandlerManager.class
                                        )
                                        public class ApiHandlerManager {
    
                                            @Reference(cardinality = ReferenceCardinality.MULTIPLE)
                                            private List<ApiHandlerService> apiHandlerServices;
    
                                            public void handlerRequests() {
                                                List<String> requestsHandeled = new ArrayList<>();
                                                for(ApiHandlerService apiHandlerService : apiHandlerServices){
                                                    requestsHandeled.add(apiHandlerService.callApi());
                                                }
                                                for(String requestOutput : requestsHandeled)
                                                    System.out.println(requestOutput);
                                            }
                                        }
    
                                    
                                
  • Now we will retrieve the values hence recieved from a servlet. Here particularly in this example we are showing the values in stdout.log file in crx-quickstart\logs\stdout.

                                    
                                        package com.local.core.servlets;
    
                                        import com.local.core.services.impl.ApiHandlerManager;
                                        import org.apache.sling.api.SlingHttpServletRequest;
                                        import org.apache.sling.api.SlingHttpServletResponse;
                                        import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
                                        import org.osgi.service.component.annotations.Component;
                                        import org.osgi.service.component.annotations.Reference;
    
                                        import javax.servlet.Servlet;
                                        import javax.servlet.ServletException;
                                        import java.io.IOException;
                                        import java.io.PrintWriter;
    
                                        @Component(
                                                service = Servlet.class,
                                                property = {
                                                        "sling.servlet.paths=/bin/apiHandler",
                                                        "sling.servlet.methods=GET"
                                                }
                                        )
                                        public class ApiHandlerServlet extends SlingSafeMethodsServlet {
                                            @Reference
                                            private ApiHandlerManager apiHandlerManager;
                                            @Override
                                            protected void doGet(SlingHttpServletRequest request,
                                             SlingHttpServletResponse response) throws ServletException, IOException {
                                                PrintWriter out = response.getWriter();
                                                out.println("Servlet Invoked");
                                                apiHandlerManager.handlerRequests();
                                                out.println("check logs");
                                            }
                                        }
    
                                    
                                

Conclusion

So here we have understood the concepts of factory configurations with a small example of cadinality and limit. Also we have understood how to retrieve the values of each instance via a servlet. Here we have printed the values in logs but you can have the direct access to the values in servlet itself by changing the return type of the method handlerRequests().

I hope you enjoyed the learing and have found the blog informative.


Shruti Meshram | AEM Developer
LinkedIn Email